package com.haixiaoke.saas.bill.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSONObject;
import com.haixiaoke.saas.bill.domain.HouseBill;
import com.haixiaoke.saas.bill.domain.HouseBillItemMerge;
import com.haixiaoke.saas.bill.mapper.HouseBillMapper;
import com.haixiaoke.saas.bill.service.IHouseBillItemMergeService;
import com.haixiaoke.saas.bill.service.IHouseBillService;
import com.haixiaoke.saas.common.constant.BillConstants;
import com.haixiaoke.saas.common.constant.OrderConstants;
import com.haixiaoke.saas.common.core.redis.RedisCache;
import com.haixiaoke.saas.common.enums.BillStateEnum;
import com.haixiaoke.saas.common.enums.OrderStateEnum;
import com.haixiaoke.saas.common.exception.ServiceException;
import com.haixiaoke.saas.common.tenant.TenantContext;
import com.haixiaoke.saas.common.utils.DateUtils;
import com.haixiaoke.saas.common.utils.bean.BeanUtils;
import com.haixiaoke.saas.common.weChat.domain.ParseParam;
import com.haixiaoke.saas.contract.domain.LesseeContract;
import com.haixiaoke.saas.house.domain.House;
import com.haixiaoke.saas.house.service.IHouseService;
import com.haixiaoke.saas.order.domain.Order;
import com.haixiaoke.saas.order.domain.OrderLessee;
import com.haixiaoke.saas.order.domain.WechatPayRecord;
import com.haixiaoke.saas.order.service.IOrderLesseeService;
import com.haixiaoke.saas.order.service.IOrderService;
import com.haixiaoke.saas.order.service.IWechatPayRecordService;
import com.haixiaoke.saas.user.domain.UserConsumer;
import com.haixiaoke.saas.user.service.IUserConsumerService;
import org.apache.commons.lang3.RandomUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.stream.Collectors;

import static com.haixiaoke.saas.system.framework.datasource.DynamicDataSourceContextHolder.log;
import static com.haixiaoke.saas.system.framework.security.util.SecurityUtils.getDeptId;
import static com.haixiaoke.saas.system.framework.security.util.SecurityUtils.getUsername;

/**
 * 入住账单Service业务层处理
 *
 * @author qixi
 * @date 2024-05-30
 */
@Service
public class HouseBillServiceImpl implements IHouseBillService {
    @Autowired
    private HouseBillMapper houseBillMapper;

    @Autowired
    private IHouseBillItemMergeService houseBillItemMergeService;
    @Autowired
    private IOrderLesseeService orderLesseeService;
    @Autowired
    private IHouseService houseService;
    @Autowired
    private RedisCache redisCache;
    @Autowired
    private IWechatPayRecordService wechatPayRecordService;
    @Autowired
    private IUserConsumerService userConsumerService;
    @Autowired
    private IOrderService orderService;


    /**
     * 查询入住账单
     *
     * @param billId 入住账单主键
     * @return 入住账单
     */
    @Override
    public HouseBill selectHouseBillByBillId(String billId) {
        List<HouseBillItemMerge> houseBillItemMerges = houseBillItemMergeService.selectHouseBillItemMergeByBillId(billId);
        HouseBill houseBill = houseBillMapper.selectHouseBillByBillId(billId);
        houseBill.setHouseBillItemMerges(houseBillItemMerges);

        BigDecimal otherCost = new BigDecimal(0);
        BigDecimal penalty = new BigDecimal(0);

        for (HouseBillItemMerge houseBillItemMerge : houseBillItemMerges) {
            if (StrUtil.equals(houseBillItemMerge.getItemType(), BillStateEnum.BILL_ITEM_TYPE_OTHER_COST.getValue())) {
                otherCost = otherCost.add(houseBillItemMerge.getItemPrice());
            } else if (StrUtil.equals(houseBillItemMerge.getItemType(), BillStateEnum.BILL_ITEM_TYPE_PENALTY.getValue())) {
                penalty = penalty.add(houseBillItemMerge.getItemPrice());
            }
        }
        houseBill.setOtherCost(otherCost);
        houseBill.setPenalty(penalty);
        return houseBill;
    }

    /**
     * 查询入住账单列表
     *
     * @param houseBill 入住账单
     * @return 入住账单
     */
    @Override
    public List<HouseBill> selectHouseBillList(HouseBill houseBill) {
        List<HouseBill> houseBills = houseBillMapper.selectHouseBillList(houseBill);
        return getHouseBillItemMergeList(houseBills);
    }

    private List<HouseBill> getHouseBillItemMergeList(List<HouseBill> houseBills) {
        if (CollectionUtil.isEmpty(houseBills)) {
            return houseBills;
        }
        List<String> billIds = houseBills.stream().map(HouseBill::getBillId).collect(Collectors.toList());
        List<HouseBillItemMerge> houseBillItemMerges = houseBillItemMergeService.selectHouseBillItemMergeByBillIds(billIds);
        Map<String, List<HouseBillItemMerge>> collect1 = houseBillItemMerges.stream().collect(Collectors.groupingBy(HouseBillItemMerge::getBillId));
        houseBills.forEach(bill -> bill.setHouseBillItemMerges(collect1.get(bill.getBillId())));
        return houseBills;
    }

    /**
     * 新增入住账单
     *
     * @param houseBill 入住账单
     * @return 结果
     */
    @Override
    public int insertHouseBill(HouseBill houseBill) {
        houseBill.setCreateTime(DateUtils.getNowDate());
        return houseBillMapper.insertHouseBill(houseBill);
    }

    /**
     * 修改入住账单
     *
     * @param houseBill 入住账单
     * @return 结果
     */
    @Override
    public int updateHouseBill(HouseBill houseBill) {
        houseBill.setUpdateTime(DateUtils.getNowDate());
        return houseBillMapper.updateHouseBill(houseBill);
    }

    /**
     * 批量删除入住账单
     *
     * @param billIds 需要删除的入住账单主键
     * @return 结果
     */
    @Override
    public int deleteHouseBillByBillIds(String[] billIds) {
        return houseBillMapper.deleteHouseBillByBillIds(billIds);
    }

    /**
     * 删除入住账单信息
     *
     * @param billId 入住账单主键
     * @return 结果
     */
    @Override
    public int deleteHouseBillByBillId(String billId) {
        return houseBillMapper.deleteHouseBillByBillId(billId);
    }

    @Override
    @Transactional
    public int createHouseBill(HouseBill houseBill) {
        String penaltyStates = houseBill.getPenaltyStates();
        if (StrUtil.equals(BillStateEnum.PENALTY_STATES_COLLECT.getValue(), penaltyStates)) {
            BigDecimal penaltyScale = houseBill.getPenaltyScale();
            if (!(penaltyScale.compareTo(BigDecimal.ZERO) > 0)) {
                throw new ServiceException("违约金比例不能小于0");
            }
        }
        List<HouseBillItemMerge> houseBillItemMerges = houseBill.getHouseBillItemMerges();
        if (CollectionUtil.isEmpty(houseBillItemMerges)) {
            throw new ServiceException("请添加费用");
        }
        List<BigDecimal> itemPrices = houseBillItemMerges.stream().map(HouseBillItemMerge::getItemPrice).filter(itemPrice -> itemPrice.compareTo(BigDecimal.ZERO) <= 0).collect(Collectors.toList());
        if (itemPrices.size() > 0) {
            throw new ServiceException("费用价格不能小于等于0");
        }

        buildHouseBill(houseBill);

        BigDecimal billCost = new BigDecimal(0);
        for (HouseBillItemMerge houseBillItemMerge : houseBillItemMerges) {
            houseBillItemMerge.setBillId(houseBill.getBillId());
            houseBillItemMerge.setItemType(BillStateEnum.BILL_ITEM_TYPE_OTHER_COST.getValue());
            houseBillItemMerge.setChargeMethod(BillStateEnum.CHARGE_METHOD_FULL_PAYMENT.getValue());
            houseBillItemMerge.setCreateTime(DateUtils.getNowDate());
            billCost = billCost.add(houseBillItemMerge.getItemPrice());
        }
        int i = houseBillItemMergeService.batchInsertHouseBillItemMerges(houseBillItemMerges);
        if (houseBillItemMerges.size() != i) {
            throw new ServiceException("费用添加失败");
        }
        houseBill.setCost(billCost);
        return insertHouseBill(houseBill);
    }

    @Override
    public void houseBillActivationTask() {
        List<HouseBill> houseBills = houseBillMapper.selectNotActivatedHouseBillList();
        List<String> ids = houseBills.stream()
                .filter(houseBill -> DateUtil.compare(houseBill.getBillCreateTime(), new Date()) <= 0)
                .map(HouseBill::getBillId).collect(Collectors.toList());
        if (CollectionUtil.isEmpty(ids)) {
            return;
        }
        houseBillMapper.updateHouseBillBillEffectiveStates(ids, BillStateEnum.BILL_EFFECTIVE_STATES_ON.getValue());
    }

    @Override
    public List<HouseBill> selectInvalidHouseBillList(HouseBill houseBill) {
        return houseBillMapper.selectInvalidHouseBillList(houseBill);
    }

    @Override
    @Transactional
    public int disassembleHouseBill(HouseBill houseBill) {
        String billId = houseBill.getBillId();
        HouseBill houseBill2 = houseBillMapper.selectHouseBillByBillId(billId);
        if (houseBill2 == null) {
            throw new ServiceException("账单不存在");
        }
        String payState = houseBill2.getPayState();
        if (!(StrUtil.equals(BillStateEnum.BILL_STATE_UNPAID.getValue(), payState)
                || StrUtil.equals(BillStateEnum.BILL_STATE_OVERDUE.getValue(), payState))) {
            throw new ServiceException("账单数据异常，请刷新后再试");
        }

        HouseBill houseBill1 = new HouseBill();
        BeanUtils.copyPropertiesIfNull(houseBill2, houseBill1, "houseBillItemMerges");
        String billId1 = "HB" + System.currentTimeMillis() + RandomUtils.nextInt(10000, 100000);
        houseBill1.setBillId(billId1);

        // 拆分账单项目
        List<HouseBillItemMerge> houseBillItemMerges = houseBill.getHouseBillItemMerges();
        List<HouseBillItemMerge> itemMerges = houseBillItemMerges.stream().filter(item -> StrUtil.equals(item.getItemType(), BillStateEnum.BILL_ITEM_TYPE_OTHER_COST.getValue())).collect(Collectors.toList());
        BigDecimal reduce = itemMerges.stream()
                .map(HouseBillItemMerge::getDisassembleFee)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        if (reduce.compareTo(houseBill2.getCost()) >= 0) {
            throw new ServiceException("拆分金额有误");
        }
        // 计算拆分出的收费项
        List<HouseBillItemMerge> houseBillItemMergeList = new ArrayList<>();
        BigDecimal billCost = new BigDecimal(0);
        for (HouseBillItemMerge houseBillItemMerge : itemMerges) {
            BigDecimal disassembleFee = houseBillItemMerge.getDisassembleFee();

            if (disassembleFee.compareTo(BigDecimal.ZERO) <= 0) {
                continue;
            }
            HouseBillItemMerge item = new HouseBillItemMerge();
            BeanUtil.copyProperties(houseBillItemMerge, item);
            item.setMergeId(null);
            item.setBillId(billId1);
            item.setItemPrice(disassembleFee);
            item.setCreateTime(DateUtils.getNowDate());
            houseBillItemMergeList.add(item);

            BigDecimal itemPrice = houseBillItemMerge.getItemPrice();
            houseBillItemMerge.setItemPrice(itemPrice.subtract(disassembleFee));
            billCost = billCost.add(disassembleFee);
        }

        houseBillItemMergeService.batchInsertHouseBillItemMerges(houseBillItemMergeList);
        houseBillItemMergeService.batchUpdateHouseBillItemMerges(itemMerges);

        houseBill1.setCost(billCost);
        houseBill1.setCreateTime(DateUtils.getNowDate());
        houseBill1.setCreateBy(getUsername());
        this.insertHouseBill(houseBill1);

        List<HouseBillItemMerge> houseBillItemMerges1 = houseBillItemMergeService.selectHouseBillItemMergeByBillId(billId);
        BigDecimal sum = houseBillItemMerges1.stream()
                .map(HouseBillItemMerge::getItemPrice)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        houseBill.setCost(sum);
        return houseBillMapper.updateHouseBill(houseBill);
    }

    private void buildHouseBill(HouseBill houseBill) {
        String orderId = houseBill.getOrderId();
        OrderLessee orderLessee = orderLesseeService.selectOrderLesseeByOrderId(orderId);
        if (orderLessee == null) {
            throw new ServiceException("承租人不存在");
        }
        Date billCreateTime = houseBill.getBillCreateTime();
        if (billCreateTime == null || DateUtil.compare(billCreateTime, DateUtil.parse(DateUtil.format(DateUtils.getNowDate(), "yyyy-MM-dd"))) < 0) {
            throw new ServiceException("账单生成时间不能小于今天");
        }
        if (DateUtil.isSameDay(billCreateTime, DateUtils.getNowDate())) {
            // 账单生成时间为当天直接生效 不为当天则不生效
            houseBill.setBillEffectiveStates(BillStateEnum.BILL_EFFECTIVE_STATES_ON.getValue());
        } else {
            houseBill.setBillEffectiveStates(BillStateEnum.BILL_EFFECTIVE_STATES_OFF.getValue());
        }
        House house = houseService.selectHouseById(houseBill.getHouseId());
        if (house == null) {
            throw new ServiceException("房源不存在");
        }
        houseBill.setProprietorId(house.getProprietorUserId());
        houseBill.setProprietorName(house.getProprietorName());


        String phone = orderLessee.getPhone();
        UserConsumer userConsumer = userConsumerService.selectUserConsumerByMobile(phone);
        if (userConsumer == null) {
            throw new ServiceException("用户不存在");
        }
        houseBill.setUserId(userConsumer.getUserId());  // 入住人id
        houseBill.setUserName(StrUtil.isEmpty(userConsumer.getUserName()) ? userConsumer.getNickName() : userConsumer.getUserName());  // 入住人姓名
        houseBill.setBillType(BillStateEnum.HOUSE_BILL_TYPE_EXPENSE.getValue());
        houseBill.setBillEnableStates(BillStateEnum.BILL_ENABLE_STATES_ON.getValue());
        String billId = "HB" + System.currentTimeMillis() + RandomUtils.nextInt(10000, 100000);
        houseBill.setBillId(billId);
        DateTime dateTime = DateUtil.endOfDay(houseBill.getLatestPayTime());
        // endOfDay方法会将时间毫秒值格式化为999，mysql数据库时间毫秒值会四舍五入 自动加一秒，所以需要减去1秒
        houseBill.setLatestPayTime(DateUtil.offset(dateTime, DateField.SECOND, -1));

    }

    @Override
    public int offlinePayment(HouseBill houseBill) {
        String billId = houseBill.getBillId();
        HouseBill houseBill1 = houseBillMapper.selectHouseBillByBillId(billId);

        String payVoucher = houseBill.getPayVoucher();
        if (StrUtil.isEmpty(payVoucher)) {
            throw new ServiceException("请上传图片");
        }
        if (!BillStateEnum.BILL_STATE_UNPAID.getValue().equals(houseBill1.getPayState()) &&
                !BillStateEnum.BILL_STATE_OVERDUE.getValue().equals(houseBill1.getPayState())) {
            throw new ServiceException("账单状态异常，请刷新后再试");
        }

        houseBill1.setPayVoucher(payVoucher);
        houseBill1.setPayTime(DateUtils.getNowDate());
        houseBill1.setPayState(BillStateEnum.BILL_STATE_PAID.getValue());
        houseBill1.setPayMethod(BillStateEnum.BILL_METHOD_OFFLINE.getValue());
        houseBill1.setUpdateTime(DateUtils.getNowDate());
        return houseBillMapper.updateHouseBill(houseBill1);
    }

    @Override
    public List<HouseBill> calculate(LesseeContract lesseeContract) {

        if (CollectionUtil.isNotEmpty(lesseeContract.getHouseBillItemMergeList())) {
            // 判断加收费用金额
            boolean housePriceBoolean = lesseeContract.getHouseBillItemMergeList().stream()
                    .anyMatch(entry -> entry.getItemPrice() == null || entry.getItemPrice().compareTo(BigDecimal.ZERO) <= 0);
            if (housePriceBoolean) {
                throw new ServiceException("加收费用金额只能大于0");
            }
            boolean priceBoolean = lesseeContract.getHouseBillItemMergeList().stream()
                    .anyMatch(entry -> entry.getItemPrice().scale() > 2);
            if (priceBoolean) {
                throw new ServiceException("加收费用金额小数点最多两位小数");
            }
        }
        Order order = lesseeContract.getOrder();
        checkOrder(order);
        String rentalMode = order.getRentalMode();
        if (rentalMode.equals(OrderStateEnum.RENTAL_MODE_DISPOSABLE.getValue())) {
            // 一次性账单
            return disposableCreateBill(lesseeContract);
        } else if (rentalMode.equals(OrderStateEnum.RENTAL_MODE_MONTH.getValue())) {
            // 月租账单
            return monthCreateBill(lesseeContract);
        }
        throw new ServiceException("账单生成失败");
    }

    @Override
    public List<HouseBill> selectHouseBillByOrderId(String orderId) {
        List<HouseBill> houseBills = houseBillMapper.selectHouseBillByOrderId(orderId);
        return getHouseBillItemMergeList(houseBills);
    }

    @Override
    public int batchInsert(List<HouseBill> list) {
        return houseBillMapper.batchInsert(list);
    }

    @Override
    public int updateBillEnableStates(String lesseeContractId, String value) {
        return houseBillMapper.updateBillEnableStates(lesseeContractId, value);
    }

    /**
     * 违约金生成任务
     */
    @Override
    @Transactional
    public void penaltyBillTask() {
        List<HouseBill> list = houseBillMapper.selectOvertimeBill();
        if (CollectionUtil.isEmpty(list)) {
            return;
        }
        List<HouseBillItemMerge> merges = new ArrayList<>();
        for (HouseBill houseBill : list) {

            // 隔天生成前一天的违约金
            Date latestPayTime = houseBill.getLatestPayTime();
            DateTime billLatestPayTime = DateUtil.offset(latestPayTime, DateField.HOUR_OF_DAY, 24);
            if (DateUtil.compare(DateUtils.getNowDate(), billLatestPayTime) < 0) {
                continue;
            }

            List<HouseBillItemMerge> houseBillItemMerges = houseBillItemMergeService.selectHouseBillItemMergeByBillId(houseBill.getBillId());
            BigDecimal penaltyScale = houseBill.getPenaltyScale();
            if (penaltyScale.compareTo(BigDecimal.ZERO) <= 0) {
                continue;
            }
            HouseBillItemMerge houseBillItemMerge = new HouseBillItemMerge();
            houseBillItemMerge.setBillId(houseBill.getBillId());
            DateTime offset = DateUtil.offset(DateUtil.date(), DateField.HOUR_OF_DAY, -24);
            int month = DateUtil.month(offset) + 1;
            String itemName = month + "-" + DateUtil.dayOfMonth(offset) + "违约金";
            houseBillItemMerge.setItemName(itemName);

            // 违约金金额按照不计算违约金 * 违约金比例
            BigDecimal cost = houseBillItemMerges.stream()
                    .filter(x -> StrUtil.equals(x.getItemType(), BillStateEnum.BILL_ITEM_TYPE_OTHER_COST.getValue()))
                    .map(HouseBillItemMerge::getItemPrice)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);

            BigDecimal penaltyFee = cost.multiply(penaltyScale.divide(new BigDecimal(100))).setScale(2, RoundingMode.HALF_UP);
            houseBillItemMerge.setItemPrice(penaltyFee);
            houseBillItemMerge.setItemType(BillStateEnum.BILL_ITEM_TYPE_PENALTY.getValue());
            houseBillItemMerge.setChargeMethod(BillStateEnum.CHARGE_METHOD_FULL_PAYMENT.getValue());
            houseBillItemMerge.setCreateTime(DateUtils.getNowDate());
            merges.add(houseBillItemMerge);

            BigDecimal sum = houseBillItemMerges.stream()
                    .map(HouseBillItemMerge::getItemPrice)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            sum = sum.add(penaltyFee);
            houseBill.setCost(sum);
            houseBillMapper.updateHouseBill(houseBill);
        }
        if (!CollectionUtil.isEmpty(merges)) {
            houseBillItemMergeService.batchInsertHouseBillItemMerges(merges);
        }
    }

    @Override
    public void payCallbackBill(ParseParam parseParam) {
        String outTradeNo = parseParam.getOutTradeNo();
        String billId = StrUtil.split(outTradeNo, "_")[0];

        Object cacheObject = redisCache.getCacheObject(BillConstants.REDIS_HOUSE_BILL_KEY + billId);
        HouseBill convert = Convert.convert(HouseBill.class, cacheObject);
        // 删除redis的key，防止触发缓存过期回调时间
        redisCache.deleteObject(BillConstants.REDIS_PROPRIETOR_BILL_KEY + billId);

        HouseBill houseBill = selectHouseBillByBillId(billId);
        if (ObjectUtil.isEmpty(houseBill)) {
            log.error("查询不到账单,参数：{}", JSONObject.toJSONString(parseParam));
            return;
        }
        // 保存微信支付回到参数
        WechatPayRecord wechatPayRecord = new WechatPayRecord();
        BeanUtil.copyProperties(parseParam, wechatPayRecord);
        wechatPayRecordService.insertWechatPayRecord(wechatPayRecord);

        // 修改账单状态
        houseBill.setPayTime(wechatPayRecord.getTimeEnd());
        houseBill.setPayState(BillStateEnum.BILL_STATE_PAID.getValue());
        houseBill.setPayMethod(BillStateEnum.BILL_METHOD_ONLINE.getValue());
        houseBill.setPayUserId(convert.getPayUserId());
        houseBill.setPayUserName(convert.getPayUserName());
        houseBill.setUpdateTime(DateUtils.getNowDate());
        houseBillMapper.updateHouseBill(houseBill);
    }

    @Override
    public void yiMaPyCallbackBill(Map<String, Object> map) {
        String posSeq = ObjectUtil.toString(map.get("pos_seq"));
        String[] split = StrUtil.split(posSeq, "_");
        HouseBill houseBill = selectHouseBillByBillId(split[0]);
        if (houseBill == null) {
            log.error("翼码回调支付失败：{}", map);
            return;
        }

        Object cacheObject = redisCache.getCacheObject(BillConstants.REDIS_HOUSE_BILL_KEY + houseBill.getBillId());
        if (cacheObject == null) {
            log.error("查询不到账单,参数：{}", JSONObject.toJSONString(houseBill));
            return;
        }
        // 删除redis的key，防止触发缓存过期回调时间
        redisCache.deleteObject(BillConstants.REDIS_PROPRIETOR_BILL_KEY + houseBill.getBillId());
        HouseBill convert = Convert.convert(HouseBill.class, cacheObject);

        houseBill.setPayTime(DateUtil.parse(ObjectUtil.toString(map.get("end_time"))));
        houseBill.setPayState(BillStateEnum.BILL_STATE_PAID.getValue());
        houseBill.setPayMethod(BillStateEnum.BILL_METHOD_ONLINE.getValue());
        houseBill.setPayUserId(convert.getPayUserId());
        houseBill.setPayUserName(convert.getPayUserName());
        houseBill.setUpdateTime(DateUtils.getNowDate());
        houseBillMapper.updateHouseBill(houseBill);

        // 保存微信支付回到参数
        WechatPayRecord wechatPayRecord = new WechatPayRecord();
        wechatPayRecord.setOutTradeNo(ObjectUtil.toString(map.get("trade_no")));
        wechatPayRecord.setTransactionId(ObjectUtil.toString(map.get("atu_trade_no")));
        wechatPayRecord.setTotalFee(ObjectUtil.toString(map.get("tx_amt")));
        wechatPayRecord.setAttach(ObjectUtil.toString(map.get("pos_seq")));
        wechatPayRecord.setTimeEnd(DateUtil.date());
        wechatPayRecord.setOpenId(ObjectUtil.toString(map.get("buyer_id")));
        wechatPayRecordService.insertWechatPayRecord(wechatPayRecord);

    }

    /**
     * 账单逾期未支付 变更支付状态任务
     */
    @Override
    public void houseBillOvertimeTask() {
        List<HouseBill> list = houseBillMapper.selectTakeEffectHouseBillList();
        if (CollectionUtil.isEmpty(list)) {
            return;
        }
        List<String> ids = list.stream().map(HouseBill::getBillId).collect(Collectors.toList());
        int i = houseBillMapper.updateHouseBillPayState(ids, BillStateEnum.BILL_STATE_OVERDUE.getValue());
        log.info("入住账单逾期未支付,更新账单状态：{}条", i);
    }

    @Override
    public List<HouseBill> selectHouseBillListByLesseeContractId(String lesseeContractId) {
        List<HouseBill> houseBills = houseBillMapper.selectHouseBillListByLesseeContractId(lesseeContractId);
        return getHouseBillItemMergeList(houseBills);
    }

    @Override
    public List<HouseBill> selectHouseBillByHouseIds(String[] houseIds) {
        return houseBillMapper.selectHouseBillByHouseIds(houseIds);
    }

    @Override
    public int selectOutstandingByOrderId(String orderId) {
        return houseBillMapper.selectOutstandingByOrderId(orderId);
    }


    /**
     * 月租账单生成
     *
     * @param lesseeContract
     * @return
     */
    private List<HouseBill> monthCreateBill(LesseeContract lesseeContract) {
        Order order = lesseeContract.getOrder();
        List<HouseBill> houseBillList = new LinkedList<>();
        String houseId = lesseeContract.getHouseId();
        House house = houseService.selectHouseByHouseId(houseId);

        Long deptId = getDeptId();
        String tenantId = TenantContext.getTenantId();
        Date checkInTime = order.getCheckInTime();
        Date checkOutTime = order.getCheckOutTime();
        BigDecimal monthRent = order.getMonthRent();
        Long payNumber = order.getPayNumber();
        // 押金 = 月租金 * 押几
        BigDecimal depositNumber = monthRent.multiply(new BigDecimal(order.getDepositNumber()));

        // 新签订单才会生成押金账单
        String orderId = order.getOrderId();
        boolean isDeposit = false;
        if (StrUtil.isEmpty(orderId)) {
            isDeposit = true;
        } else {
            Order order1 = orderService.selectOrderByOrderId(orderId);
            if (ObjectUtil.isNotNull(order1)) {
                String tradingMode = order1.getTradingMode();
                if (OrderConstants.TRADING_MODE_SIGN.equals(tradingMode)) {
                    isDeposit = true;
                }
            }
        }

        if (isDeposit) {
            // 押金账单
            HouseBill houseBill = new HouseBill();
            houseBill.setBillId("HB" + System.currentTimeMillis() + RandomUtils.nextInt(10000, 100000));
            houseBill.setHouseId(houseId);
            houseBill.setTenantId(tenantId);
            houseBill.setDeptId(deptId);
            houseBill.setProprietorId(house.getProprietorUserId());
            houseBill.setProprietorName(house.getProprietorName());
            houseBill.setPayState(BillStateEnum.BILL_STATE_UNPAID.getValue());

            houseBill.setLatestPayTime(getLatestPayTime(checkInTime));
            houseBill.setBillType(BillStateEnum.HOUSE_BILL_TYPE_DEPOSIT.getValue());
            houseBill.setBillEnableStates(BillStateEnum.BILL_ENABLE_STATES_OFF.getValue());
            houseBill.setBillEffectiveStates(BillStateEnum.BILL_EFFECTIVE_STATES_OFF.getValue());

            houseBill.setBillStartTime(checkInTime);
            houseBill.setBillEndTime(checkOutTime);

            HouseBillItemMerge houseBillItemMerge = new HouseBillItemMerge();
            houseBillItemMerge.setBillId(houseBill.getBillId());
            houseBillItemMerge.setItemName("押金");
            houseBillItemMerge.setItemPrice(depositNumber);
            houseBillItemMerge.setItemType(BillStateEnum.BILL_ITEM_TYPE_OTHER_COST.getValue());
            houseBillItemMerge.setChargeMethod(BillStateEnum.CHARGE_METHOD_FULL_PAYMENT.getValue());
            houseBill.setHouseBillItemMerges(ListUtil.list(false, houseBillItemMerge));
            houseBill.setCost(depositNumber);
            houseBillList.add(houseBill);
        }


        List<Map<String, Object>> billCycleList = buildCycle(order);
        int flag = 1;
        for (Map<String, Object> map : billCycleList) {
            HouseBill bill = new HouseBill();
            bill.setBillId("HB" + System.currentTimeMillis() + RandomUtils.nextInt(10000, 100000));
            bill.setHouseId(houseId);
            bill.setTenantId(tenantId);
            bill.setDeptId(deptId);
            bill.setProprietorId(house.getProprietorUserId());
            bill.setProprietorName(house.getProprietorName());
            bill.setPayState(BillStateEnum.BILL_STATE_UNPAID.getValue());
            bill.setLatestPayTime(getLatestPayTime((Date) map.get("latestPayTime")));
            bill.setBillType(BillStateEnum.HOUSE_BILL_TYPE_EXPENSE.getValue());
            bill.setBillEnableStates(BillStateEnum.BILL_ENABLE_STATES_OFF.getValue());
            bill.setBillEffectiveStates(BillStateEnum.BILL_EFFECTIVE_STATES_OFF.getValue());
            Date startTime = (Date) map.get("startTime");
            bill.setBillStartTime(startTime);
            bill.setBillEndTime(getLatestPayTime((Date) map.get("endTime")));
            Object billCreateTime = map.get("billCreateTime");
            if (billCreateTime != null) {
                bill.setBillCreateTime((Date) billCreateTime);
            }
            List<HouseBillItemMerge> merges = this.constructionFee(checkInTime, startTime, bill.getBillId(), lesseeContract.getHouseBillItemMergeList(), monthRent, map, payNumber);
            BigDecimal cost = merges.stream()
                    .map(HouseBillItemMerge::getItemPrice)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            bill.setCost(cost);
            bill.setHouseBillItemMerges(merges);
            bill.setPeriods(flag);
            houseBillList.add(bill);
            flag++;
        }
        return houseBillList;

    }

    private List<HouseBillItemMerge> constructionFee(Date checkInTime, Date startTime, String billId, List<HouseBillItemMerge> merges, BigDecimal monthRent, Map<String, Object> map, Long payNumber) {

        List<HouseBillItemMerge> list = new ArrayList<>();
        HouseBillItemMerge rentBillItem = new HouseBillItemMerge();
        rentBillItem.setBillId(billId);
        rentBillItem.setItemName("租金");
        BigDecimal itemCost = calculateCost(map, monthRent, payNumber);
        rentBillItem.setItemPrice(itemCost);
        rentBillItem.setItemType(BillStateEnum.BILL_ITEM_TYPE_OTHER_COST.getValue());
        rentBillItem.setChargeMethod(BillStateEnum.CHARGE_METHOD_PAYMENT_WITH_RENT.getValue());
        list.add(rentBillItem);

        for (HouseBillItemMerge merge : merges) {
            String chargeMethod = merge.getChargeMethod();
            if (chargeMethod.equals(BillStateEnum.CHARGE_METHOD_FULL_PAYMENT.getValue()) && !(checkInTime.compareTo(startTime) == 0)) {
                continue;
            }

            HouseBillItemMerge houseBillItemMerge = new HouseBillItemMerge();
            houseBillItemMerge.setBillId(billId);
            houseBillItemMerge.setItemName(merge.getItemName());
            houseBillItemMerge.setItemPrice(merge.getItemPrice());
            houseBillItemMerge.setItemType(BillStateEnum.BILL_ITEM_TYPE_OTHER_COST.getValue());
            houseBillItemMerge.setChargeMethod(merge.getChargeMethod());
            list.add(houseBillItemMerge);
        }
        return list;
    }


    /**
     * 计算租金
     *
     * @param map
     * @param monthRent
     * @return
     */
    private BigDecimal calculateCost(Map<String, Object> map, BigDecimal monthRent, Long payNumber) {
        int state = (int) map.get("state");
        // 1 整租
        if (state == 1) {
            return monthRent.multiply(new BigDecimal(payNumber)).setScale(2, RoundingMode.HALF_UP);
            // 按天数计算 月租金/30*天数
        } else {
            long day = (long) map.get("day");
            BigDecimal divide = monthRent.divide(new BigDecimal(30), 2, RoundingMode.HALF_UP);
            return divide.multiply(new BigDecimal(day)).setScale(2, RoundingMode.HALF_UP);
        }
    }

    // @Override
    // public List<Map<String, Object>> buildCycle(Order order) {
    //     Date checkInTime = order.getCheckInTime();
    //     Date checkOutTime = DateUtil.endOfDay(order.getCheckOutTime());
    //     String billGenerationType = order.getBillGenerationType();
    //     int payNumber = order.getPayNumber().intValue();
    //
    //     Date startTime = checkInTime;
    //     List<Map<String, Object>> list = new LinkedList<>();
    //     for (; ; ) {
    //         Map<String, Object> map = new HashMap<>();
    //         // 合同月
    //         if (OrderStateEnum.BILL_GENERATION_TYPE_CONTRACT.getValue().equals(billGenerationType)) {
    //             map.put("startTime", startTime);
    //             DateTime offset = DateUtil.endOfDay(DateUtil.offset(startTime, DateField.MONTH, payNumber));
    //             startTime = offset;
    //             DateTime offsetEndTime = DateUtil.offset(offset, DateField.HOUR_OF_DAY, -24);
    //             if (offsetEndTime.compareTo(checkOutTime) < 0) {
    //                 map.put("endTime", offsetEndTime);
    //                 map.put("state", 1);
    //                 list.add(map);
    //             } else if (offsetEndTime.compareTo(checkOutTime) == 0) {
    //                 map.put("endTime", offsetEndTime);
    //                 map.put("state", 1);
    //                 list.add(map);
    //                 break;
    //             } else if (offsetEndTime.compareTo(checkOutTime) > 0) {
    //                 map.put("endTime", checkOutTime);
    //                 map.put("state", 2);
    //                 Date startTime1 = (Date) map.get("startTime");
    //                 long between = DateUtil.betweenDay(startTime1, checkOutTime, true) + 1;
    //                 map.put("day", between);
    //                 list.add(map);
    //                 break;
    //             }
    //
    //         } else if (OrderStateEnum.BILL_GENERATION_TYPE_NATURE.getValue().equals(billGenerationType)) {
    //             // 首月
    //             if (startTime.compareTo(checkInTime) == 0) {
    //                 map.put("startTime", startTime);
    //                 DateTime offsetMonth = DateUtil.offset(startTime, DateField.MONTH, payNumber  - 1);
    //                 DateTime endDay = DateUtil.endOfMonth(offsetMonth);
    //
    //                 if (endDay.compareTo(checkOutTime) < 0) {
    //                     map.put("endTime", endDay);
    //                     map.put("state", 1);
    //                     if (!DateUtil.isSameDay(DateUtil.beginOfMonth(startTime), startTime)) {
    //                         // 首月天数
    //                         long day = DateUtil.betweenDay(startTime, DateUtil.endOfMonth(endDay), true) + 1;
    //                         map.put("day", day);
    //                         map.put("state", 2);
    //                     }
    //                     list.add(map);
    //                 } else if (endDay.compareTo(checkOutTime) == 0) {
    //                     map.put("endTime", endDay);
    //                     map.put("state", 1);
    //                     if (!DateUtil.isSameDay(DateUtil.beginOfMonth(startTime), startTime)) {
    //                         // 首月天数
    //                         long day = DateUtil.betweenDay(startTime, DateUtil.endOfMonth(endDay), true) + 1;
    //                         map.put("day", day);
    //                         map.put("state", 2);
    //                     }
    //                     list.add(map);
    //                     break;
    //                 } else if (endDay.compareTo(checkOutTime) > 0) {
    //                     map.put("endTime", checkOutTime);
    //                     map.put("state", 2);
    //                     Date startTime1 = (Date) map.get("startTime");
    //                     long between = DateUtil.betweenDay(startTime1, checkOutTime, true) + 1;
    //                     map.put("day", between);
    //                     list.add(map);
    //                     break;
    //                 }
    //                 startTime = DateUtil.offset(endDay, DateField.HOUR_OF_DAY, 24);
    //             } else {
    //                 map.put("startTime", startTime);
    //                 DateTime offset = DateUtil.endOfMonth(DateUtil.offset(startTime, DateField.MONTH, payNumber -1));
    //                 if (offset.compareTo(checkOutTime) < 0) {
    //                     map.put("endTime", offset);
    //                     map.put("state", 1);
    //                     list.add(map);
    //                 } else if (offset.compareTo(checkOutTime) == 0) {
    //                     map.put("endTime", offset);
    //                     map.put("state", 1);
    //                     list.add(map);
    //                     break;
    //                 } else if (offset.compareTo(checkOutTime) > 0) {
    //                     map.put("endTime", checkOutTime);
    //                     map.put("state", 2);
    //                     long between = DateUtil.betweenDay(startTime, checkOutTime, true) + 1;
    //                     map.put("day", between);
    //                     list.add(map);
    //                     break;
    //                 }
    //                 startTime = DateUtil.offset(offset, DateField.HOUR_OF_DAY, 24);
    //             }
    //         }
    //     }
    //     return list;
    // }

    /**
     * 构建账单周期
     *
     * @param order
     * @return
     */
    public List<Map<String, Object>> buildCycle(Order order) {
        Date checkInTime = order.getCheckInTime();
        Date checkOutTime = DateUtil.endOfDay(order.getCheckOutTime());
        String billGenerationType = order.getBillGenerationType();
        int payNumber = order.getPayNumber().intValue();

        Date startTime = checkInTime;
        List<Map<String, Object>> list = new LinkedList<>();
        for (; ; ) {
            Map<String, Object> map = new HashMap<>();
            boolean isContract = OrderStateEnum.BILL_GENERATION_TYPE_CONTRACT.getValue().equals(billGenerationType);
            Date endTime;
            int state;
            Date latestPayTime;
            Date billCreateTime;
            if (isContract) {
                // 首月首期账单
                boolean isFirstMonth = startTime.compareTo(checkInTime) == 0;
                if (isFirstMonth) {
                    latestPayTime = startTime;
                    map.put("latestPayTime", latestPayTime);
                } else {
                    latestPayTime = buildLatestPayTime(startTime, order.getRentDayType(), order.getRentCollectionDate());
                    billCreateTime = DateUtil.offset(latestPayTime, DateField.HOUR_OF_DAY, -24 * order.getBillGenerationDate());
                    map.put("latestPayTime", latestPayTime);
                    map.put("billCreateTime", billCreateTime);
                }

                map.put("startTime", startTime);
                Date offset = DateUtil.endOfDay(DateUtil.offset(startTime, DateField.MONTH, payNumber));
                Date offsetEndTime = DateUtil.offset(offset, DateField.HOUR_OF_DAY, -24);
                if (offsetEndTime.compareTo(checkOutTime) < 0) {
                    endTime = offsetEndTime;
                    state = 1;
                    map.put("endTime", endTime);
                    map.put("state", state);
                    list.add(map);
                } else if (offsetEndTime.compareTo(checkOutTime) == 0) {
                    endTime = offsetEndTime;
                    state = 1;
                    map.put("endTime", endTime);
                    map.put("state", state);
                    list.add(map);
                    break;
                } else {
                    endTime = checkOutTime;
                    state = 2;
                    map.put("endTime", endTime);
                    map.put("state", state);
                    long between = DateUtil.betweenDay(startTime, checkOutTime, true) + 1;
                    map.put("day", between);
                    list.add(map);
                    break;
                }
                startTime = offset;
            } else {
                // 首月
                if (startTime.compareTo(checkInTime) == 0) {
                    latestPayTime = startTime;
                    map.put("latestPayTime", latestPayTime);

                    map.put("startTime", startTime);
                    DateTime offsetMonth = DateUtil.offset(startTime, DateField.MONTH, payNumber - 1);
                    DateTime endDay = DateUtil.endOfMonth(offsetMonth);

                    if (endDay.compareTo(checkOutTime) < 0) {
                        map.put("endTime", endDay);
                        map.put("state", 1);
                        long day = calculateFirstMonthDays(startTime, endDay);
                        if (day > 0) {
                            map.put("day", day);
                            map.put("state", 2);
                        }
                        list.add(map);
                    } else if (endDay.compareTo(checkOutTime) == 0) {
                        map.put("endTime", endDay);
                        map.put("state", 1);
                        long day = calculateFirstMonthDays(startTime, endDay);
                        if (day > 0) {
                            map.put("day", day);
                            map.put("state", 2);
                        }
                        list.add(map);
                        break;
                    } else if (endDay.compareTo(checkOutTime) > 0) {
                        map.put("endTime", checkOutTime);
                        map.put("state", 2);
                        Date startTime1 = (Date) map.get("startTime");
                        long between = DateUtil.betweenDay(startTime1, checkOutTime, true) + 1;
                        map.put("day", between);
                        list.add(map);
                        break;
                    }
                    startTime = DateUtil.offset(endDay, DateField.HOUR_OF_DAY, 24);
                } else {

                    latestPayTime = buildLatestPayTime(startTime, order.getRentDayType(), order.getRentCollectionDate());
                    billCreateTime = DateUtil.offset(latestPayTime, DateField.HOUR_OF_DAY, -24 * order.getBillGenerationDate());
                    map.put("latestPayTime", latestPayTime);
                    map.put("billCreateTime", billCreateTime);

                    map.put("startTime", startTime);
                    DateTime offset = DateUtil.endOfMonth(DateUtil.offset(startTime, DateField.MONTH, payNumber - 1));

                    if (offset.compareTo(checkOutTime) < 0) {
                        map.put("endTime", offset);
                        map.put("state", 1);
                        list.add(map);
                    } else if (offset.compareTo(checkOutTime) == 0) {
                        map.put("endTime", offset);
                        map.put("state", 1);
                        list.add(map);
                        break;
                    } else if (offset.compareTo(checkOutTime) > 0) {
                        map.put("endTime", checkOutTime);
                        map.put("state", 2);
                        long between = DateUtil.betweenDay(startTime, checkOutTime, true) + 1;
                        map.put("day", between);
                        list.add(map);
                        break;
                    }
                    startTime = DateUtil.offset(offset, DateField.HOUR_OF_DAY, 24);
                }
                //
                // // 首月
                // if (startTime.compareTo(checkInTime) == 0) {
                //     map.put("startTime", startTime);
                //     DateTime offsetMonth = DateUtil.offset(startTime, DateField.MONTH, payNumber  - 1);
                //     DateTime endDay = DateUtil.endOfMonth(offsetMonth);
                //
                //     if (endDay.compareTo(checkOutTime) < 0) {
                //         map.put("endTime", endDay);
                //         map.put("state", 1);
                //         if (!DateUtil.isSameDay(DateUtil.beginOfMonth(startTime), startTime)) {
                //             // 首月天数
                //             long day = DateUtil.betweenDay(startTime, DateUtil.endOfMonth(endDay), true) + 1;
                //             map.put("day", day);
                //             map.put("state", 2);
                //         }
                //         list.add(map);
                //     } else if (endDay.compareTo(checkOutTime) == 0) {
                //         map.put("endTime", endDay);
                //         map.put("state", 1);
                //         if (!DateUtil.isSameDay(DateUtil.beginOfMonth(startTime), startTime)) {
                //             // 首月天数
                //             long day = DateUtil.betweenDay(startTime, DateUtil.endOfMonth(endDay), true) + 1;
                //             map.put("day", day);
                //             map.put("state", 2);
                //         }
                //         list.add(map);
                //         break;
                //     } else if (endDay.compareTo(checkOutTime) > 0) {
                //         map.put("endTime", checkOutTime);
                //         map.put("state", 2);
                //         Date startTime1 = (Date) map.get("startTime");
                //         long between = DateUtil.betweenDay(startTime1, checkOutTime, true) + 1;
                //         map.put("day", between);
                //         list.add(map);
                //         break;
                //     }
                //     startTime = DateUtil.offset(endDay, DateField.HOUR_OF_DAY, 24);
                // } else {
                //     map.put("startTime", startTime);
                //     DateTime offset = DateUtil.endOfMonth(DateUtil.offset(startTime, DateField.MONTH, payNumber -1));
                //     if (offset.compareTo(checkOutTime) < 0) {
                //         map.put("endTime", offset);
                //         map.put("state", 1);
                //         list.add(map);
                //     } else if (offset.compareTo(checkOutTime) == 0) {
                //         map.put("endTime", offset);
                //         map.put("state", 1);
                //         list.add(map);
                //         break;
                //     } else if (offset.compareTo(checkOutTime) > 0) {
                //         map.put("endTime", checkOutTime);
                //         map.put("state", 2);
                //         long between = DateUtil.betweenDay(startTime, checkOutTime, true) + 1;
                //         map.put("day", between);
                //         list.add(map);
                //         break;
                //     }
                //     startTime = DateUtil.offset(offset, DateField.HOUR_OF_DAY, 24);
                // }
            }
        }
        return list;
    }

    private long calculateFirstMonthDays(Date startTime, Date endDay) {
        if (!DateUtil.isSameDay(DateUtil.beginOfMonth(startTime), startTime)) {
            return DateUtil.betweenDay(startTime, DateUtil.endOfMonth(endDay), true) + 1;
        }
        return 0;
    }

    private Date buildLatestPayTime(Date startTime, String rentDayType, Integer rentCollectionDate) {
        Date latestPayTime = DateUtil.date();
        switch (rentDayType) {
            case OrderConstants.ADVANCE_EACH_PERIOD:
                latestPayTime = DateUtil.endOfDay(DateUtil.offset(startTime, DateField.HOUR_OF_DAY, rentCollectionDate * -24));
                break;
            case OrderConstants.FIRST_MONTH_EACH_PERIOD:
                latestPayTime = DateUtil.endOfDay(DateUtil.offset(DateUtil.beginOfDay(startTime), DateField.HOUR_OF_DAY, rentCollectionDate));
                break;
            case OrderConstants.ADVANCE_ONE_MONTH:
                int offsetTime = rentCollectionDate == 1 ? 24 : (rentCollectionDate - 1) * 24;
                latestPayTime = DateUtil.endOfDay(DateUtil.offset(DateUtil.beginOfMonth(DateUtil.offset(startTime, DateField.MONTH, -1)), DateField.HOUR_OF_DAY, offsetTime));
                break;
        }
        return latestPayTime;
    }

    // 一次性账单
    private List<HouseBill> disposableCreateBill(LesseeContract lesseeContract) {
        Order order = lesseeContract.getOrder();
        List<HouseBill> houseBillList = new LinkedList<>();
        String houseId = lesseeContract.getHouseId();
        House house = houseService.selectHouseByHouseId(houseId);

        Date checkInTime = order.getCheckInTime();
        // 最晚付款日期 = 入住时间
        DateTime latestPayTime = getLatestPayTime(checkInTime);
        // 押金
        HouseBill depositBill = new HouseBill();
        depositBill.setBillId("HB" + System.currentTimeMillis() + RandomUtils.nextInt(10000, 100000));
        depositBill.setHouseId(houseId);
        depositBill.setTenantId(TenantContext.getTenantId());
        depositBill.setDeptId(getDeptId());
        depositBill.setProprietorId(house.getProprietorUserId());
        depositBill.setProprietorName(house.getProprietorName());
        depositBill.setCost(order.getDeposit());
        depositBill.setLatestPayTime(latestPayTime);
        depositBill.setBillType(BillStateEnum.HOUSE_BILL_TYPE_DEPOSIT.getValue());
        depositBill.setBillEnableStates(BillStateEnum.BILL_ENABLE_STATES_OFF.getValue());
        depositBill.setBillEffectiveStates(BillStateEnum.BILL_EFFECTIVE_STATES_OFF.getValue());
        depositBill.setBillStartTime(checkInTime);
        depositBill.setBillEndTime(order.getCheckOutTime());
        depositBill.setPayState(BillStateEnum.BILL_STATE_UNPAID.getValue());

        HouseBillItemMerge houseBillItemMerge = new HouseBillItemMerge();
        houseBillItemMerge.setItemName("押金");
        houseBillItemMerge.setItemPrice(order.getDeposit());
        houseBillItemMerge.setItemType(BillStateEnum.BILL_ITEM_TYPE_OTHER_COST.getValue());
        houseBillItemMerge.setChargeMethod(BillStateEnum.CHARGE_METHOD_FULL_PAYMENT.getValue());
        houseBillItemMerge.setBillId(depositBill.getBillId());
        depositBill.setHouseBillItemMerges(ListUtil.list(false, houseBillItemMerge));
        depositBill.setPeriods(0);
        houseBillList.add(depositBill);

        HouseBill otherBill = new HouseBill();
        otherBill.setBillId("HB" + System.currentTimeMillis() + RandomUtils.nextInt(10000, 100000));
        otherBill.setHouseId(houseId);
        otherBill.setTenantId(TenantContext.getTenantId());
        otherBill.setDeptId(getDeptId());
        otherBill.setProprietorId(house.getProprietorUserId());
        otherBill.setProprietorName(house.getProprietorName());
        otherBill.setLatestPayTime(latestPayTime);
        otherBill.setBillType(BillStateEnum.HOUSE_BILL_TYPE_EXPENSE.getValue());
        otherBill.setBillEnableStates(BillStateEnum.BILL_ENABLE_STATES_OFF.getValue());
        otherBill.setBillEffectiveStates(BillStateEnum.BILL_EFFECTIVE_STATES_OFF.getValue());
        otherBill.setBillStartTime(checkInTime);
        otherBill.setBillEndTime(order.getCheckOutTime());
        otherBill.setPayState(BillStateEnum.BILL_STATE_UNPAID.getValue());
        List<HouseBillItemMerge> houseBillItemMergeList = lesseeContract.getHouseBillItemMergeList();
        BigDecimal totalRent = order.getTotalRent();

        HouseBillItemMerge houseBillItemMerge1 = new HouseBillItemMerge();
        houseBillItemMerge1.setItemName("租金");
        houseBillItemMerge1.setItemPrice(totalRent);
        houseBillItemMerge1.setBillId(otherBill.getBillId());
        houseBillItemMerge1.setItemType(BillStateEnum.BILL_ITEM_TYPE_OTHER_COST.getValue());
        houseBillItemMerge1.setChargeMethod(BillStateEnum.CHARGE_METHOD_FULL_PAYMENT.getValue());
        for (HouseBillItemMerge billItemMerge : houseBillItemMergeList) {
            BigDecimal itemPrice = billItemMerge.getItemPrice();
            billItemMerge.setBillId(otherBill.getBillId());
            billItemMerge.setItemType(BillStateEnum.BILL_ITEM_TYPE_OTHER_COST.getValue());
            billItemMerge.setChargeMethod(BillStateEnum.CHARGE_METHOD_FULL_PAYMENT.getValue());
            totalRent = totalRent.add(itemPrice);
        }
        houseBillItemMergeList.add(houseBillItemMerge1);
        otherBill.setCost(totalRent);
        otherBill.setPeriods(1);
        otherBill.setHouseBillItemMerges(houseBillItemMergeList);
        houseBillList.add(otherBill);
        return houseBillList;
    }

    private DateTime getLatestPayTime(Date checkInTime) {
        // endOfDay方法会将时间毫秒值格式化为999，mysql数据库时间毫秒值会四舍五入 自动加一秒，所以需要减去1秒
        DateTime dateTime = DateUtil.endOfDay(checkInTime);
        return DateUtil.offset(dateTime, DateField.SECOND, -1);
    }


    private void checkOrder(Order order) {
        if (ObjectUtil.isEmpty(order)) {
            throw new ServiceException("订单不存在");
        }
        String rentalMode = order.getRentalMode();
        if (StrUtil.isBlank(rentalMode)) {
            throw new ServiceException("付款方式不能为空");
        }
        if (ObjectUtil.isNull(order.getCheckInTime()) || ObjectUtil.isNull(order.getCheckOutTime())) {
            throw new ServiceException("入离时间不能为空");
        }
        // 付款方式
        if (rentalMode.equals(OrderStateEnum.RENTAL_MODE_MONTH.getValue())) {
            if (order.getDepositNumber() == null || order.getPayNumber() == null) {
                throw new ServiceException("押付方式不能为空");
            }

            // 月租金
            if (order.getMonthRent() == null || order.getMonthRent().compareTo(BigDecimal.ZERO) < 0) {
                throw new ServiceException("月租金不能小于0");
            }
            // 押金
            // if (order.getDeposit() == null || order.getDeposit().compareTo(BigDecimal.ZERO) < 0) {
            //     throw new ServiceException("押金不能小于0");
            // }

            // 收租日
            if (StrUtil.isEmpty(order.getRentDayType()) || order.getRentCollectionDate() == null
                    || order.getRentCollectionDate() < 1 || order.getRentCollectionDate() > 31) {
                throw new ServiceException("收租日有误");
            }

            // 账单生成日
            if (order.getBillGenerationDate() == null || order.getBillGenerationDate() < 0) {
                throw new ServiceException("账单生成日有误");
            }


            // 账单生成规则
            if (StrUtil.isEmpty(order.getBillGenerationType())) {
                throw new ServiceException("账单生成规则有误");
            }
        } else if (rentalMode.equals(OrderStateEnum.RENTAL_MODE_DISPOSABLE.getValue())) {

            if (order.getTotalRent() == null || order.getTotalRent().compareTo(BigDecimal.ZERO) < 0) {
                throw new ServiceException("总租金有误");
            }

            // 押金
            if (order.getDeposit() == null || order.getDeposit().compareTo(BigDecimal.ZERO) < 0) {
                throw new ServiceException("押金不能小于0");
            }
        }
    }


}
